﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using PSTaskDialog;

namespace ArduinoSketch.Core
{
    public delegate void CompileProjectAsyncMethod(ProjectEntity projectArgs);
    public delegate void UploadAsyncMethod(ProjectEntity projectArgs);

    public delegate void BeginCompileAsync();
    public delegate void CompilingAsync(int progress);
    public delegate void EndCompileAsync();   

    /// <summary>
    /// 
    /// </summary>
    public class Compiler
    {
        #region Compile Callback
        public static BeginCompileAsync BeginCompile = null;
        public static CompilingAsync Compiling = null;
        public static EndCompileAsync EndCompile = null;
        #endregion

        #region Compile Parameter
        private string gcc = "";
        private string gpp = "";
        private string ar = "";
        private string objcp = "";
        private string avrsize = "";
        private string libraries = "";

        private string mcu = "";
        private string df_cpu = "";
        private string applet = "";
        private string ardu = "";
        private string ardu_core = "";
        private string baudrate = "";
        private string avrdude = String.Format(@"{0}\avrdude.exe", Application.StartupPath); 
        private string potocal = "";
        private string port = "com1";
        private string avrdude_mcu = "";

        private HardwareProfile _profile;
        private AvrdudeUploader _uploader = new AvrdudeUploader();

        #endregion

        private StringBuilder objLinker;

        private IOutputConsole _console;
        private bool _compileError = false;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="console"></param>
        public Compiler(IOutputConsole console)
        {
            this._console = console;
        }

        private void MessageErrorDialog(string Title, string MainInstruction, string Content)
        {
            //PSTaskDialog.cTaskDialog.MessageBox(
            //                Title,
            //                MainInstruction,
            //                Content,
            //                eTaskDialogButtons.OK,
            //                eSysIcons.Error);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="profileName"></param>
        private bool LoadHardwareTarget(string profileName)
        {
            _profile = Utils.GetHardwareProfile(profileName);
            if (_profile != null)
            {
                this.ardu_core = string.Format(@"{0}\hardware\cores\{1}", ardu, _profile.Core);
                this.mcu = _profile.Mcu;
                this.df_cpu = _profile.Fcpu;
                this.baudrate = _profile.UploadSpeed;
                this.potocal = _profile.UploadPotocal;

                avrdude_mcu = this.mcu.Replace("atmega", " ").Trim();

                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        public void SetPort(string value)
        {
            this.port = value;
            this._uploader.SetCurrentPortName(value);
        }

        #region Compiling Progress

        /// <summary>
        /// 
        /// </summary>
        private void DoBeginCompile()
        {
            if (Compiler.BeginCompile != null)
            {
                if (Compiler.BeginCompile.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = Compiler.BeginCompile.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(Compiler.BeginCompile);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="progress"></param>
        private void DoCompilingProgress(int progress)
        {
            if (Compiler.Compiling != null)
            {
                if (Compiler.Compiling.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = Compiler.Compiling.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(Compiler.Compiling, new object[] { progress });
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void DoEndcompile()
        {
            this.DoCompilingProgress(100);
            if (Compiler.EndCompile != null)
            {
                if (Compiler.EndCompile.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = Compiler.EndCompile.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(Compiler.EndCompile);
                }
            }

            InvokeMessage(new string[] { this._strSketchSize });
        }

        #endregion
        
        /// <summary>
        /// Set avr gcc environment for compile
        /// </summary>
        private void SetEnvironment()
        {
            try
            {
                ArduinoTargetConfig _arduinoTarget = Utils.LoadArduinoTargetConfig();

                this.ardu = _arduinoTarget.ArduinoDirectory;

                this.gcc = string.Format(@"{0}\hardware\tools\avr\bin\avr-gcc", _arduinoTarget.ArduinoDirectory);
                this.gpp = string.Format(@"{0}\hardware\tools\avr\bin\avr-g++", _arduinoTarget.ArduinoDirectory);
                this.ar = string.Format(@"{0}\hardware\tools\avr\bin\avr-ar", _arduinoTarget.ArduinoDirectory);
                this.objcp = string.Format(@"{0}\hardware\tools\avr\bin\avr-objcopy", _arduinoTarget.ArduinoDirectory);
                this.avrsize = string.Format(@"{0}\hardware\tools\avr\bin\avr-size", _arduinoTarget.ArduinoDirectory);
                this.libraries = _arduinoTarget.ArduinoLibDirectory;
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// compile project
        /// </summary>
        /// <param name="project"></param>
        public void Compile(ProjectEntity project)
        {
            try
            {
                if (this._console.InvokeClearConsole.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = this._console.InvokeClearConsole.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(this._console.InvokeClearConsole);
                }
                this._compileError = false;
                // compile sketch 
                this.DoCompile(project);
                // Remove temporary file
                Directory.Delete(applet, true);

                this.DoEndcompile();
                
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// upload project
        /// </summary>
        /// <param name="project"></param>
        public void Upload(ProjectEntity project)
        {
            try
            {
                if (this._console.InvokeClearConsole.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = this._console.InvokeClearConsole.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(this._console.InvokeClearConsole);
                }

                this._compileError = false;
                // compile sketch  
                this.DoCompilingProgress(10);
                this.DoCompile(project);
                // upload sketch 
                this.DoCompilingProgress(90);
                DoUpload(project);

                this.DoCompilingProgress(95);
                this.DoEndcompile();
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        private void GetCOM(int index)
        {
            try
            {
                string[] strArray = DeviceInfo.ParsePorts();

                if (strArray[index] != "")
                {
                    string SelectedPort = strArray[index];
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }       
        }

        #region Compile and Upload
        /// <summary>
        /// 
        /// </summary>
        /// <param name="project"></param>
        private void DoUpload(ProjectEntity project)
        {
            string _fileName = ""; 
            string _applet = string.Format(@"{0}/{1}", project.ProjectPath, "applet");

            try
            {
                DirectoryInfo _dirInfo = new DirectoryInfo(_applet);
                FileInfo[] _fileInfos = _dirInfo.GetFiles();

                this.InvokeMessage(new string[] { "Upload sketch ..." });

                foreach (var _file in _fileInfos)
                {
                    if (_file.Extension.Equals(".hex"))
                    {
                        _fileName = _file.FullName;
                        break;
                    }
                }

                // Upload hex file by avrdude
                this.InvokeMessage(new string[] { "  " + avrdude });
                this.MessageAvrDude(_uploader.uploadViaBootloader(_profile, _fileName));
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="project"></param>
        private void DoCompile(ProjectEntity project)
        {
            string _appletPath = String.Empty;
            string _strCpp = String.Empty;
            string _strPdeName = String.Empty;
            DirectoryInfo _dirInfo = null;

            try
            {
                this.DoBeginCompile();

                this.SetEnvironment();
                this.LoadHardwareTarget(project.HardwareProfile);
                this.InvokeMessage(new string[] { "Compile sketch ..." });

                this.DoCompilingProgress(10);

                _dirInfo = new DirectoryInfo(project.ProjectPath);
                // Create build output
                _appletPath = string.Format(@"{0}\{1}", project.ProjectPath, "applet");
                if (!Directory.Exists(_appletPath))
                {
                    Directory.CreateDirectory(_appletPath);
                }
                else
                {
                    Directory.Delete(_appletPath, true);
                    Directory.CreateDirectory(_appletPath);
                }

                foreach (var _file in _dirInfo.GetFiles())
                {
                    string _fileName = _file.FullName;
                    if (_file.Extension.Equals(".pde"))
                    {
                        _strPdeName = _file.Name.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries)[0];
                        _strCpp = this.ConvertPedToCpp(_file.FullName);
                    }
                    else if (_file.Extension.Equals(".cpp") ||
                        _file.Extension.Equals(".c") ||
                        _file.Extension.Equals(".h"))
                    {
                        // Move File to applet
                        string destFileName = string.Format(@"{0}\applet\{1}", _file.DirectoryName, _file.Name);
                        File.Copy(_file.FullName, destFileName, true);
                    }
                }

                string _cppName = string.Format(@"{0}\{1}.cpp", _appletPath, _strPdeName);
                this.WriteToFile(_strCpp, _cppName);
                this.CompileSketch(_cppName);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="fileName"></param>
        private void WriteToFile(string input, string fileName)
        {
            try
            {
                using (FileStream fs = File.Create(fileName))
                {
                    Encoding _encoding = Encoding.GetEncoding("TIS-620");
                    using (BinaryWriter bw = new BinaryWriter(fs, _encoding))
                    {
                        byte[] valueByte = _encoding.GetBytes(input);
                        bw.Write(valueByte, 0, valueByte.Length - 1);
                    }
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }
        #endregion

        #region Convert sketch file to cpp
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pdeFileName"></param>
        /// <returns></returns>
        private string ConvertPedToCpp(string pdeFileName)
        {
            StringBuilder _cppBuilder = new StringBuilder();
            string _x = "";
            string _program = "";
            try
            {             
                Encoding _encoding = Encoding.GetEncoding("TIS-620");
                using (Stream fsr = new FileStream(pdeFileName, FileMode.Open))
                {
                    using (StreamReader sr = new StreamReader(fsr, _encoding))
                    {
                        int _idx = 0;
                        this._includeFiles.Clear();
                        _program = "#include <WProgram.h>\r\n" + sr.ReadToEnd();

                        Regex r = new Regex("(^\\s*#(?:\\\\\\n|.)*)", 
                            RegexOptions.Multiline);
                        Match m = r.Match(_program);

                        int i = 0;
                        
                        while (m != null && m.Success)
                        {
                            _x += m.Groups[0].Value;
                            _idx = m.Index + m.Groups[0].Value.Length;
                            Console.Out.Write(m.Groups[0].Value);
                            m = m.NextMatch();
                        }

                        //_idx = _x.TrimEnd().Length + 2;
                        _x = _program.Substring(0, _idx); 
                        _cppBuilder.AppendLine(_program);
 
                        // Create prototype function
                        r = new Regex("^\\s*[\\w\\[\\]\\*]+\\s+[\\[\\]\\*\\w\\s]+\\([,\\[\\]\\*\\w\\s]*\\)(?=\\s*\\{)", RegexOptions.Multiline);
                        m = r.Match(_program);
                        _cppBuilder.Insert(_idx, "\r\n"); _idx += 2;
                        while (m != null && m.Success)
                        {
                            string _str = m.Groups[0].Value + ";\n";
                            _cppBuilder.Insert(_idx, _str);
                            _idx += _str.Length;
                            m = m.NextMatch();
                        }
                        _cppBuilder.Insert(_idx, "\r\n"); 
                        //
                        sr.Close();
                        
                        _cppBuilder.AppendLine();
                        _cppBuilder.AppendLine("int main(void)");
                        _cppBuilder.AppendLine("{");
                        _cppBuilder.AppendLine("\tinit();");
                        _cppBuilder.AppendLine("\tsetup();");
                        _cppBuilder.AppendLine("\tfor (;;)");
                        _cppBuilder.AppendLine("\t\tloop();");
                        _cppBuilder.AppendLine("\treturn 0;");
                        _cppBuilder.AppendLine("}");
                        _cppBuilder.AppendLine();
                    }
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }

            this.LoadIncludeFile(_program);

            return _cppBuilder.ToString(); ;
        }

        private List<string> _includeFiles = new List<string>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="strLine"></param>
        private void LoadIncludeFile(string strLine)
        {
            Regex r = new Regex("\\s*#include\\s+[<\"](\\S+)[\">]");
            Match m = r.Match(strLine);          

            while (m != null && m.Success)
            {
                this._includeFiles.Add(m.Groups[1].Value);
                m = m.NextMatch();
            }
        }

        #endregion

        #region Compile arduino core library

        /// <summary>
        /// 
        /// </summary>
        /// <param name="strInclude"></param>
        private void CompileLib(string strInclude)
        {
            try
            {
                string[] fileInfo = Directory.GetFiles(this.ardu_core);
                string _objFileName = "";
                StringBuilder _objectFileNames = new StringBuilder();
                this.DoCompilingProgress(40);
                foreach (var file in fileInfo)
                {
                    FileInfo _info = new FileInfo(file);
                    if (_info.Extension.Equals(".c"))
                    {
                        _objFileName = String.Format(@"{0}\{1}.o", applet, _info.Name);
                        this.RunProcess(gcc, StringConst.BuildGccCommand(new object[] { mcu, df_cpu, strInclude, _info.FullName, _objFileName }));
                        //this.RunProcess(ar, StringConst.LinkingCommand(new object[] { applet, _objFileName }));

                        _objectFileNames.Append(_objFileName + " ");
                    }
                }
                this.DoCompilingProgress(50);
                foreach (var file in fileInfo)
                {
                    FileInfo _info = new FileInfo(file);
                    if (_info.Extension.Equals(".cpp"))
                    {
                        _objFileName = String.Format(@"{0}\{1}.o", applet, _info.Name);
                        this.RunProcess(gpp, StringConst.BuildGccCommand(new object[] { mcu, df_cpu, strInclude, _info.FullName, _objFileName }));
                        //this.RunProcess(ar, StringConst.LinkingCommand(new object[] { applet, _objFileName }));
                        _objectFileNames.Append(_objFileName + " ");
                    }
                }
                // Linker Library
                this.DoCompilingProgress(55);
                _objFileName = _objectFileNames.ToString();
                this.RunProcess(ar, StringConst.LinkingCommand(new object[] { applet, _objFileName }));
                ///
                this.DoCompilingProgress(60);
                this.CompileIncludeLib(strInclude);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }
        #endregion

        #region Compile Include Library

        /// <summary>
        /// 
        /// </summary>
        /// <param name="strInclude"></param>
        private void CompileIncludeLib(string strInclude)
        {
            try
            {
                if (this._includeFiles.Count == 0) { return; }

                DirectoryInfo _dirInfo = new DirectoryInfo(libraries);
                DirectoryInfo[] _libDirs = _dirInfo.GetDirectories();
                FileInfo[] _libFileInfo = null;
                string _objFileName = "";
                List<DirectoryInfo> _libDirInfos = new List<DirectoryInfo>();
                foreach (var _dir in _libDirs)
                {
                    _libFileInfo = _dir.GetFiles();
                    foreach (var _libFile in _libFileInfo)
                    {
                        this._includeFiles.ForEach(e => { if (_libFile.Name.Equals(e)) { _libDirInfos.Add(_dir); } });
                    }
                }

                _libDirInfos.ForEach(e =>
                {
                    _libFileInfo = e.GetFiles();
                    // Compile .c
                    foreach (var _libFile in _libFileInfo)
                    {
                        if (_libFile.Extension.Equals(".c"))
                        {
                            _objFileName = _libFile.FullName + ".o";
                            this.RunProcess(gcc, string.Format(StringConst.GCC_INCLUDE_LIB, new object[] { mcu, df_cpu, strInclude, _libFile.FullName, _objFileName }));
                            this.objLinker.Append(_objFileName + " ");
                        }
                    }
                    // Compile .cpp
                    foreach (var _libFile in _libFileInfo)
                    {
                        if (_libFile.Extension.Equals(".cpp"))
                        {
                            _objFileName = _libFile.FullName + ".o";
                            this.RunProcess(gpp, string.Format(StringConst.GPP_INCLUDE_LIB, new object[] { mcu, df_cpu, strInclude, _libFile.FullName, _objFileName }));
                            this.objLinker.Append(_objFileName + " ");
                        }
                    }

                    this.CompileIncludeUtilityLib(e.FullName, strInclude);
                });
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            } 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="utility"></param>
        /// <param name="strInclude"></param>
        private void CompileIncludeUtilityLib(string utility, string strInclude)
        {
            string _utility = string.Format(@"{0}\utility", utility);
            string _objFileName = "";

            try
            {
                if (Directory.Exists(_utility))
                {
                    DirectoryInfo _utilityDirinfo = new DirectoryInfo(_utility);
                    List<FileInfo> _utilityFileInfo = _utilityDirinfo.GetFiles().ToList<FileInfo>();
                    _utilityFileInfo.ForEach(e =>
                    {
                        if (e.Extension.Equals(".c"))
                        {
                            _objFileName = e.FullName + ".o";
                            this.RunProcess(gcc, string.Format(StringConst.GCC_INCLUDE_LIB, new object[] { mcu, df_cpu, strInclude, e.FullName, _objFileName }));
                            this.objLinker.Append(_objFileName + " ");

                        }
                    });
                    _utilityFileInfo.ForEach(e =>
                    {
                        if (e.Extension.Equals(".cpp"))
                        {
                            _objFileName = e.FullName + ".o";
                            this.RunProcess(gpp, string.Format(StringConst.GCC_INCLUDE_LIB, new object[] { mcu, df_cpu, strInclude, e.FullName, _objFileName }));
                            this.objLinker.Append(_objFileName + " ");

                        }
                    });
                } 
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
                            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private string GetIncludeDirectory()
        {
            string _includeFile = string.Format(" -I{0}\\hardware\\cores\\{1}", ardu, _profile.Core);
            string _libDir = "";

            try
            {
                DirectoryInfo _dirInfo = new DirectoryInfo(libraries);
                DirectoryInfo[] _libDirs = _dirInfo.GetDirectories();

                foreach (var _include in _includeFiles)
                {
                    foreach (var libDirName in _libDirs)
                    {
                        FileInfo[] _fileInfo = libDirName.GetFiles();
                        foreach (var file in _fileInfo)
                        {
                            if (file.Name.Equals(_include))
                            {
                                _libDir = string.Format("{0}\\hardware\\libraries\\{1}", ardu, libDirName.Name);
                                _includeFile += string.Format(" -I{0}", _libDir);

                                string _utility = string.Format("{0}\\utility", _libDir);
                                if (Directory.Exists(_utility))
                                {
                                    //_includeFile += " -I" + _utility + " ";
                                    _includeFile += string.Format(" -I{0}\\utility", _libDir);
                                }
                            }
                        }
                    }
                }
                //_includeFile += " -I" + applet;            
                _includeFile += string.Format(" -I{0}", applet);
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }

            return _includeFile;
        }
        #endregion

        #region Compile Sketch
        private void CompileSketch(string cppName)
        {
            try
            {
                FileInfo fi = new FileInfo(cppName);
                string filename = fi.FullName;
                applet = fi.DirectoryName + @"\";

                DirectoryInfo _dirInfo = new DirectoryInfo(applet);
                FileInfo[] _fileInfos = _dirInfo.GetFiles();
                string _include = this.GetIncludeDirectory();
                string _objFileName = "";

                objLinker = new StringBuilder();
                this.DoCompilingProgress(20);
                //  Compile source file
                foreach (var _file in _fileInfos)
                {
                    if (_file.Extension.Equals(".cpp"))
                    {
                        _objFileName = string.Format("{0}.o", _file.FullName);
                        this.RunProcess(gpp, StringConst.BuildSkethCommand(new object[] { mcu, df_cpu, _include, _file.FullName, _objFileName }));
                        this.objLinker.Append(_objFileName + " ");
                    }
                    if (this._compileError) { return; }
                }

                // Compile custom library
                this.DoCompilingProgress(30);
                this.CompileLib(_include);
                if (this._compileError) { return; }
                System.Threading.Thread.Sleep(1000);
                // Object linker
                this.DoCompilingProgress(70);
                this.RunProcess(gcc, string.Format(StringConst.GPP_LIMKER, new object[] { mcu, filename, objLinker.ToString(), applet }));
                if (this._compileError) { return; }

                // Create flash image .hex
                this.DoCompilingProgress(75);
                this.RunProcess(objcp, string.Format(StringConst.CREATE_FLASH_IMAGE, new object[] { filename, filename }));
                if (this._compileError) { return; }

                // Create eeprom image .eep
                this.DoCompilingProgress(80);
                this.RunProcess(objcp, string.Format(StringConst.CREATE_EEPROM_IMAGE, new object[] { filename, filename }));
                if (this._compileError) { return; }

                // Show flash image size
                this.DoCompilingProgress(85);
                this.RunProcess(avrsize, string.Format(StringConst.PRINT_SIZE, new object[] { mcu, filename }));
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }
        #endregion

        #region Run Process
        private string RunProcess(string filename, string arguments)
        {
            System.IO.StreamReader sr;
            Process builder = new Process();

            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine(arguments);
            builder.StartInfo.RedirectStandardOutput = true;
            builder.StartInfo.RedirectStandardError = true;
            builder.StartInfo.UseShellExecute = false;
            builder.StartInfo.CreateNoWindow = true;
            builder.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            builder.StartInfo.FileName = filename;
            builder.StartInfo.Arguments = arguments;
            builder.Start();
            builder.WaitForExit();
            sr = builder.StandardOutput;
            string _strOutput = sr.ReadToEnd();
            if (!string.IsNullOrEmpty(_strOutput))
                Console.Out.WriteLine(_strOutput);

            if (_strOutput.IndexOf("AVR Memory Usage") != -1)
                MessageAvrSize(_strOutput);

            sr = builder.StandardError;
            string _errorLine = sr.ReadToEnd();

            if (!String.IsNullOrEmpty(_errorLine))
            {
                this._compileError = true;
                MessageError(_errorLine);
            }
            return "";
        }
        #endregion

        #region Message output infomation
        private void MessageError(string str)
        {
            string[] messages;
            try
            {
                messages = str.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

                if (this._console.InvokeMessageInfo.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = this._console.InvokeMessageInfo.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(this._console.InvokeMessageInfo, new object[] { messages });
                }
            }
            catch (Exception ex)
            {

                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        private void MessageAvrDude(string str)
        {
            string[] messages ;
            try
            {
                if (str.IndexOf("done") != -1)
                {
                    messages = str.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    if (messages.Length > 4)
                        this.InvokeMessage(new string[] { "  " + messages[7] });
                    else
                    {
                        foreach (var _txt in messages)
                            this.InvokeMessage(new string[] { "  " + _txt });
                    }
                }
                else
                {
                    this.InvokeMessage(new string[] { "  " + str });
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }

        private string _strSketchSize = "";
        private void MessageAvrSize(string str)
        {
            string[] messages;

            try
            {
                messages = str.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                this.InvokeMessage(new string[] { "  " + messages[2] });
                this.InvokeMessage(new string[] { "  " + messages[3] });
                this.InvokeMessage(new string[] { "  " + messages[5] });

                Regex r = new Regex("\\S+(\\d+)(\\S+)");
                Match m = r.Match(messages[3]);
                int _sketchSize = Int32.Parse(m.Groups[0].Value.Trim());
                _strSketchSize = string.Format("Binary sketch size: {0} bytes (of a {1} byte maximum)", _sketchSize, _profile.UploadMaximum);
                Console.Out.WriteLine(_strSketchSize);
                if (_sketchSize > Int32.Parse(_profile.UploadMaximum))
                {
                    this._compileError = true;
                    _strSketchSize = "";
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
           
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="messages"></param>
        private void InvokeMessage(string[] messages)
        {
            try
            {
                if (this._console.InvokeMessageInfo.Target is System.Windows.Forms.Control)
                {
                    System.Windows.Forms.Control _ctrl = this._console.InvokeMessageInfo.Target as System.Windows.Forms.Control;
                    _ctrl.Invoke(this._console.InvokeMessageInfo, new object[] { messages });
                }
            }
            catch (Exception ex)
            {
                this.MessageErrorDialog("Error", "Arduino Sketch.NET", ex.Message);
            }
            
        }
        #endregion
    }
}
